# -*- coding: utf-8 -*-
# @ Time   :  2020/1/14 9:31
# @ Author : Redtree
# @ File :  oauth2_tool
# @ Desc : Oauth2认证工具


import base64
import hmac
import json
import time
import logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(name)s:%(message)s')

from flask import request

from configs.sys_config import TOKEN_EXPIRE
from infrastructure.utils.http import responser


# 生成token
def generate_token(key):
    r"""
    @Args:
        key: str (用户给定的key，需要用户保存以便之后验证token,每次产生token时的key 都可以是同一个key)
        expire: int(最大有效时间，单位为s)
    @Return:
        state: str
    """
    logger = logging.getLogger(__name__)
    try:
        ts_str = str(time.time() + int(TOKEN_EXPIRE))
        ts_byte = ts_str.encode("utf-8")
        sha1_tshexstr = hmac.new(str(key).encode("utf-8"), ts_byte, "sha1").hexdigest()
        token = ts_str + ":" + sha1_tshexstr
        b64_token = base64.urlsafe_b64encode(token.encode("utf-8"))
        return b64_token.decode("utf-8")
    except Exception as e:
        print("token生成报错:" + str(e), flush=True)
        return False


# token校验
def certify_token(key, token):
    r"""
    @Args:
        key: str
        token: str
    @Returns:
        boolean
    """
    try:
        token_str = base64.urlsafe_b64decode(token).decode("utf-8")
        token_list = token_str.split(":")
        if len(token_list) != 2:
            return False
        ts_str = token_list[0]
        # 校验token是否超时
        if float(ts_str) < time.time():
            # token expired
            return False
        known_sha1_tsstr = str(token_list[1])
        sha1 = hmac.new(str(key).encode("utf-8"), ts_str.encode("utf-8"), "sha1")
        calc_sha1_tsstr = sha1.hexdigest()
        if calc_sha1_tsstr != known_sha1_tsstr:
            # token certification failed
            return False
        # token certification success
        return True
    except:
        return False


def request2json(request):
    if request.form:
        return request.form.to_dict()
    else:
        return json.loads(request.data)


# Oauth2认证装饰器
"""
这里使用的认证规则是 发起请求的用户名+发起请求的客户端类型代码 作为认证密钥，后期可以修改。
暂时不对真实的action_client_code作校验。
"""
def oauth2_check(func):
    def inner(*args, **kwargs):
        try:
            if request.method == "GET":
                rjson = request.args.to_dict()
                key = rjson["userId"]
                token = rjson["token"]
                if certify_token(key, token) == True:
                    # 认证通过则不拦截
                    pass
                else:
                    # 超时登出,异步更新超时登出日志
                    return responser.send(20001)
            elif request.method == "POST":
                rjson = request2json(request)
                key = rjson["userId"]
                token = rjson["token"]
                # print("Oauth2认证:" + str(key) + "、" + str(token), flush=True)
                if certify_token(key, token) == True:
                    # 认证通过则不拦截
                    pass
                else:
                    # 超时登出
                    return responser.send(20001)
            else:
                # 暂不支持验证其他请求
                return responser.send(30001)
        except Exception as e:
            print("Oauth2认证失败:" + str(e))
            return responser.send(30001)
        return func(*args, **kwargs)

    return inner
